import {
  booleanAttrGetter,
  booleanAttrSetter,
  colorAttrGetter,
  colorAttrSetter,
  enumAttrGetter,
  enumAttrSetter,
  hrefAttrGetter,
  hrefAttrSetter,
  HREF_DATA,
  HREF_TARGET,
  lengthAttrGetter,
  lengthAttrSetter,
} from './attribute'
import { PEElement } from './element'

export interface BooleanCommand {
  type: 'boolean'
  linkage: false | string
}

export interface LengthCommand {
  type: 'length'
  linkage: false | string
}

export interface ColorCommand {
  type: 'color'
  linkage: false | string
}

export interface EnumCommand {
  type: 'enum'
  linkage: false | string
  enum: string[]
}

export interface HrefCommand {
  type: 'href'
  linkage: false | string
}

export interface CommandOptions {
  [propName: string]: BooleanCommand | LengthCommand | ColorCommand | EnumCommand | HrefCommand
}

export interface Commands {
  [propName: string]: HREF_DATA | boolean | string | null
}

export default class Command {
  #el: PEElement

  #option: CommandOptions = {}

  /**
   * 需要进行 style attribute 联动的指令名
   */
  #needLinkage: Map<string, string> = new Map()

  public get options() {
    return this.#option
  }

  /**
   * 配置指令设置的规则
   */
  public set options(data) {
    this.#option = data
    this.#needLinkage.clear()
    Object.entries(data).forEach(([command, { linkage }]) => {
      linkage && this.#needLinkage.set(command, linkage)
    })
  }

  constructor(el: PEElement) {
    this.#el = el
  }

  /**
   * 执行指令
   * @param command 指令名称
   * @param value 指令 配置/值
   */
  public do(command: string, value: HREF_DATA | boolean | string | null) {
    if (command in this.options) {
      const option = this.options[command]
      switch (option.type) {
        case 'boolean':
          booleanAttrSetter(this.#el, command, value as boolean)
          break
        case 'color':
          colorAttrSetter(this.#el, command, value as string)
          break
        case 'enum':
          enumAttrSetter(this.#el, command, value as string, option.enum)
          break
        case 'length':
          lengthAttrSetter(this.#el, command, value as string)
          break
        case 'href':
          hrefAttrSetter(this.#el, value as HREF_DATA)
          break
      }
      // 进行 style attribute 联动
      const linkage = this.#needLinkage.get(command)
      if (linkage) {
        this.#el.style.setProperty(linkage, value as string)
      }
    }
  }

  /**
   * 撤销指令
   * @param command 指令名
   */
  public undo(command: string) {
    const option = this.options[command]
    if (option) {
      switch (option.type) {
        case 'href':
          hrefAttrSetter(this.#el, null)
          break
        default:
          this.#el.removeAttribute(command)
          break
      }
      // 进行 style attribute 联动
      const linkage = this.#needLinkage.get(command)
      if (linkage) {
        this.#el.style.removeProperty(linkage)
      }
    }
  }

  /**
   * 清除指令
   */
  public clear() {
    Object.keys(this.options).forEach((command) => this.undo(command))
  }

  /**
   * 获取节点已设置的指令配置
   */
  public get commands() {
    const temp: Commands = {}

    this.#el.getAttributeNames().forEach((name) => {
      if (name in this.options) {
        const option = this.options[name]
        let value: HREF_DATA | boolean | string | null = null
        switch (option.type) {
          case 'boolean':
            value = booleanAttrGetter(this.#el, name)
            break
          case 'color':
            value = colorAttrGetter(this.#el, name)
            break
          case 'enum':
            value = enumAttrGetter(this.#el, name, option.enum)
            break
          case 'length':
            value = lengthAttrGetter(this.#el, name)
            break
          case 'href':
            value = hrefAttrGetter(this.#el)
            break
        }
        if (value !== null) {
          temp[name] = value
        }
      }
    })
    return temp
  }

  /**
   * 批量执行指令
   */
  public set commands(data) {
    Object.entries(data).forEach(([command, value]) => {
      this.do(command, value)
    })
  }
}
